package pl.enigmatic.reflection;

public class NestedPropertySetter implements Setter {

	private final Getter nested;
	private final Setter simple;
	private final PropertyPath path;

	public NestedPropertySetter(final Class<?> cls, final PropertyPath path, final Class<?> type) throws Exception {
		this.path = path;
		if (path.isSimple()) {
			nested = new Getter() {

				@Override
				public Object get(final Object target) {
					return target;
				}

				@Override
				public Class<?> getType() {
					return cls;
				}

				@Override
				public PropertyPath getPath() {
					return new PropertyPath("");
				}
			};
		} else {
			nested = new NestedPropertyGetter(cls, path.suffixed()) {

				@Override
				protected Getter createGetter(final Class<?> cls, final String property) {
					return NestedPropertySetter.this.createGetter(cls, property);
				}
			};
		}
		final String property = path.last();
		Setter setter = null;
		try {
			setter = new SimpleSetter(nested.getType(), property, type);
		} catch (final Exception e) {
			try {
				setter = new PublicPropertySetter(cls, property);
			} catch (final Exception e2) {
				setter = createSetter(cls, property, type);
			}
		}
		if (setter == null) { throw new Exception(String.format("No setter found for property '%1$s' in class '%2$s'", property, cls.getName())); }
		simple = setter;
	}

	protected Getter createGetter(final Class<?> cls, final String property) {
		return null;
	}

	protected Setter createSetter(final Class<?> cls, final String property, final Class<?> type) {
		return null;
	}

	@Override
	public boolean set(final Object target, final Object value) {
		final Object trg = nested.get(target);
		if (trg != null) {
			return simple.set(trg, value);
		} else {
			return false;
		}
	}

	@Override
	public Class<?> getType() {
		return simple.getType();
	}

	@Override
	public PropertyPath getPath() {
		return path;
	}

}
